// ============================================================================
// FileName: SIPDomains.cs
//
// Description:
// Maintains a list of domains and domain aliases that can be used by various
// SIP Server agents. For example allows a SIP Registrar or Proxy to check the 
// domain on an incoming request to see if it is serviced at this location.
//
// Author(s):
// Aaron Clauson
//
// History:
// 27 Jul 2008	Aaron Clauson	Created.
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2006-2008 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
// ============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using BlueFace.Sys.Net;
using BlueFace.VoIP.Authentication;
using BlueFace.VoIP.Net;
using BlueFace.VoIP.Net.SIP;
using BlueFace.Sys;
using log4net;

#if UNITTEST
using NUnit.Framework;
#endif

namespace BlueFace.VoIP.SIPServer
{
    /// <summary>
    /// This class maintains a list of domains that are being maintained by this process.
    /// </summary>
    public class SIPDomains
    {
        private ILog logger = AppState.logger;

        private List<string> m_domains = new List<string>();           // Records the domains that are being maintained.

        private Dictionary<string, string> m_domainAliases = new Dictionary<string, string>(); // List of aliases for the domains <alias, domain>.

        public bool HasDomain(string domain)
        {
            if (domain == null)
            {
                return false;
            }
            else
            {
                return m_domains.Contains(domain.ToLower()) || m_domainAliases.ContainsKey(domain.ToLower());
            }
        }

        public void AddDomainAlias(string alias, string domain)
        {
            if (alias == null || alias.Trim().Length == 0)
            {
                logger.Error("Cannot add domain alias for an empty alias.");
            }
            else if (domain == null || domain.Trim().Length == 0)
            {
                logger.Error("Cannot add domain alise for an empty domain.");
            }
            else if (!m_domainAliases.ContainsKey(alias.ToLower()))
            {
                m_domainAliases.Add(alias.ToLower(), domain.ToLower());
            }
            else
            {
                logger.Error("Cannot add domain alias for " + alias.ToLower() + " as it already exists to " + m_domainAliases[alias].ToLower() + ".");
            }
        }

        public void AddDomain(string domain)
        {
            if (domain != null && !m_domains.Contains(domain.ToLower()))
            {
                m_domains.Add(domain.ToLower());
            }
        }

        public void RemoveDomain(string domain)
        {
            if (domain == null || domain.Trim().Length == 0)
            {
                logger.Error("Cannot add remove empty domain.");
            }
            else
            {
                if (m_domainAliases.ContainsKey(domain.ToLower()))
                {
                    m_domainAliases.Remove(domain.ToLower());
                }
                else if (m_domains.Contains(domain.ToLower()))
                {
                    lock (m_domainAliases)
                    {
                        List<string> aliasesToRemove = new List<string>();
                        foreach (KeyValuePair<string, string> entry in m_domainAliases)
                        {
                            if (domain == entry.Value)
                            {
                                aliasesToRemove.Add(entry.Key);
                            }
                        }

                        foreach (string removal in aliasesToRemove)
                        {
                            m_domainAliases.Remove(removal);
                        }
                    }

                    m_domains.Remove(domain);
                }
            }
        }

        public string GetDomain(string alias)
        {
            if (alias == null || alias.Trim().Length == 0)
            {
                return null;
            }
            else
            {
                if (m_domains.Contains(alias.ToLower()))
                {
                    return alias.ToLower();
                }
                else if (m_domainAliases.ContainsKey(alias.ToLower()))
                {
                    return m_domainAliases[alias.ToLower()];
                }
                else
                {
                    return null;
                }
            }
        }
    }
}
